Spring Security介绍与应用
Spring Security
Spring security介绍
1.认证(authentication)与授权(access-control)
基本的权限控制流程
- 用户使用账号密码登录
- 校验验证码与密码
- 获取用户信息
- 为用户创建权限存储对象
- 后续访问时的权限控制
前三步为认证,后两步为授权
认证
- 通过AbstractAuthenticationProcessingFilter过滤器将账号密码组装成Authentication实现类UsernamePasswordAuthenticationToken;
- 将token传递给AuthenticationManager验证是否有效,而AuthenticationManager通常使用ProviderManager实现类来检验;
- AuthenticationManager认证成功后将返回一个拥有详细信息的Authentication object(包括权限信息,身份信息,细节信息,但密码通常会被移除);
- 通过SecurityContextHolder.getContext().getAuthentication().getPrincipal()将Authentication设置到security context中;
授权
- 通过FilterSecurityInterceptor过滤器入口进入;
- FilterSecurityInterceptor通过其继承的抽象类的AbstractSecurityInterceptor.beforeInvocation(Object,object)方法进行访问授权,其中涉及了类AuthenticationManager、AccessDecisionManager、SecurityMetadataSource等。
2.Spring Security核心组件
-
SecurityContextHolder
提供对SecurityContext的访问,存储security context(用户信息、角色权限等),有一个名为principal的Object对象,这个Object默认为UserDetail对象,也可以自定义用户对象。
-
Authentication
描述当前用户的相关信息,其包含用户拥有的权限信息列表、用户细节信息。通常为UsernamePasswordAuthenticationToken
-
UserDetails&GrantedAuthority
UserDetails提供从数据源构建Authentication对象所需的信息,包含GrantedAuthority。
UserDetails与Authentication接口功能类似,其实含义即是Authentication为用户提交的认证凭证(账号密码),UserDetails为系统中用户正确认证凭证,在UserDetailsService中的loadUserByUsername方法获取正确的认证凭证。
3.Spring Security核心服务类
-
AuthenticationManager、ProviderManager以及AuthenticationProvider
AuthenticationManager是认证相关的核心接口,是认证一切的起点。但常见的认证流程都是AuthenticationManager实现类ProviderManager处理,而且ProviderManager实现类基于委托者模式维护AuthenticationProvider 列表用于不同的认证方式。
ProviderManager中的AuthenticationProvider列表,会依照次序去认证,默认策略下,只需要通过一个AuthenticationProvider的认证,即可被认为是登录成功,而且AuthenticationProvider认证成功后返回一个Authentication实体,并为了安全会进行清除密码。如果所有认证器都无法认证成功,ProviderManager会抛出一个ProviderNotFoundException异常。
-
UserDetailsService
UserDetailsService接口作用是从特定的地方获取认证的数据源(账号、密码)。如何获取到系统中正确的认证凭证,通过loadUserByUsername(String username)获取认证信息
-
AccessDecisionManager&SecurityMetadataSource
由AbstractSecurityInterceptor调用,负责做出最终的访问控制决策。
-
PasswordEncoder
密码加密接口,实现类有使用BCrypt hash算法实现的BCryptPasswordEncoder,SCrypt hashing 算法实现的SCryptPasswordEncoder实现类
4.Spring Security核心过滤器
-
FilterSecurityInterceptor
该类根据访问的用户的角色,权限授权访问那些资源
-
UsernamePasswordAuthenticationFilter
最常用的过滤器,根据请求中包含的账号、密码,组建UsernamePasswordAuthenticationToken的Authentication实体
认证时,调用AuthenticationManager进行认证,根据认证结果返回成功或者失败
只有在使用formLogin方法之后才会有
-
AnonymousAuthenticationFilter
它位于常用的身份认证过滤器(如UsernamePasswordAuthenticationFilter、BasicAuthenticationFilter、RememberMeAuthenticationFilter)之后,意味着只有在上述身份过滤器执行完毕后,SecurityContext依旧没有用户信息,AnonymousAuthenticationFilter该过滤器才会有意义——基于用户一个匿名身份。
-
SecurityContextPersistenceFilter
SecurityContextPersistenceFilter的两个主要作用便是request来临时,创建SecurityContext安全上下文信息和request结束时清空SecurityContextHolder。
5.Spring Security认证流程
Spring security配置
1.WebSecurityConfigurer
- AuthenticationManagerBuilder:用来配置全局的认证相关的信息,其实就是AuthenticationProvider和UserDetailsService,前者是认证服务提供商,后者是用户详情查询服务;
- WebSecurity: 全局请求忽略规则配置(比如说静态文件,比如说注册页面)、全局HttpFirewall配置、是否debug配置、全局SecurityFilterChain配置、privilegeEvaluator、expressionHandler、securityInterceptor;
- HttpSecurity:具体的权限控制规则配置。一个这个配置相当于xml配置中的一个标签。各种具体的认证机制的相关配置,OpenIDLoginConfigurer、AnonymousConfigurer、FormLoginConfigurer、HttpBasicConfigurer等。
webSecurity和httpSecurity的关系
在filterChainProxy中,维护着每种请求对应的filter拦截链,每次会遍历一下这个列表,找到不同url请求对应的拦截链。如果在webSecurity中配置了ignoring,则没有对应的拦截链。那么就会调用application注册的拦截器,包括Springboot自带的拦截器与@component注册的拦截器。不同url对应的拦截器则由httpSecurity进行配置与注册。
protected void configure(AuthenticationManagerBuilder auth) throws Exception {}
public void configure(WebSecurity web) throws Exception {}
protected void configure(HttpSecurity httpSecurity) throws Exception {} 2.filter执行顺序
| Filter class | 说明 |
|---|---|
| ChannelProcessingFilter | 访问协议控制过滤器,可能会将我们重新定向到另外一种协议,从http转换成https |
| SecurityContextPersistenceFilter | 创建SecurityContext安全上下文信息和request结束时清空SecurityContextHolder |
| ConcurrentSessionFilter | 并发访问控制过滤器,主要功能:SessionRegistry中获取SessionInformation来判断session是否过期,从而实现并发访问控制。 |
| HeaderWriterFilter | 给http response添加一些Header |
| CsrfFilter | 跨域过滤器,跨站请求伪造保护Filter |
| LogoutFilter | 处理退出登录的Filter |
| X509AuthenticationFilter | 添加X509预授权处理机制支持 |
| CasAuthenticationFilter | 认证filter,经过这些过滤器后SecurityContextHolder中将包含一个完全组装好的Authentication对象,从而使后续鉴权能正常执行 |
| UsernamePasswordAuthenticationFilter | 认证的filter,经过这些过滤器后SecurityContextHolder中将包含一个完全组装好的Authentication对象,从而使后续鉴权能正常执行。表单认证是最常用的一个认证方式。 |
| BasicAuthenticationFilter | 认证filter,经过这些过滤器后SecurityContextHolder中将包含一个完全组装好的Authentication对象,从而使后续鉴权能正常执行 |
| SecurityContextHolderAwareRequestFilter | 此过滤器对ServletRequest进行了一次包装,使得request具有更加丰富的API |
| JaasApiIntegrationFilter | (JAAS)认证方式filter |
| RememberMeAuthenticationFilter | 记忆认证处理过滤器,即是如果前面认证过滤器没有对当前的请求进行处理,启用了RememberMe功能,会从cookie中解析出用户,并进行认证处理,之后在SecurityContextHolder中存入一个Authentication对象。 |
| AnonymousAuthenticationFilter | 匿名认证处理过滤器,当SecurityContextHolder中认证信息为空,则会创建一个匿名用户存入到SecurityContextHolder中 |
| SessionManagementFilter | 会话管理Filter,持久化用户登录信息,可以保存到session中,也可以保存到cookie或者redis中 |
| ExceptionTranslationFilter | 异常处理过滤器,主要拦截后续过滤器(FilterSecurityInterceptor)操作中抛出的异常。 |
| FilterSecurityInterceptor | 安全拦截过滤器类,获取当前请求url对应的ConfigAttribute,并调用accessDecisionManager进行访问授权决策。 |
PCMC中的应用
1.认证实现
登录前
- VerityCodeFilter:验证码认证器
- LoginPerFilter:单点登录,nologin链接登录时的认证器
登录验证
- UsernamePasswordAuthenticationFilter:spring security的账号密码认证器,只有在登录url时生效,验证输入的账号密码是否正确
登录后
- JwtAuthorizationTokenFilter:校验输入的token里的用户,去redis里拿对应的保存的登陆用户
- RestRequestFilter:Rest请求的认证器,认证其渠道信息是否正确
认证流程图
2.授权实现
功能点权限
流程图
数据权限
- DataPermissionInterceptor:数据权限的mybatis拦截器,会在mybatis的sql执行前进行拦截。
- 拦截器拿到查询SQL的本体PlainSelect以及入参paramHashMap
- 将入参中permissionEntity的权限规则转换成表达式的集合88
- 遍历入参paramHashMap的键值对,获取permissionEntity权限实体类
- 根据实体类中的角色编号、请求Url(或功能编号)、请求参数获取对应的权限规则与规则明细集合
- 遍历这些权限规则,将对应的规则明细转换成对应的表达式类
- 遍历每个权限规则明细,放入当前用户编号
- 根据规则明细中的操作符,调用对应的方法进行解析,每个操作符对应一个操作类
- 解析规则明细中的字段名,作为操作类的左边表达式
- 根据规则明细中的规则类型,调用对应的类解析值,作为操作类的右边表达式
- 将第3步的表达式与规则明细中的连接符,组装成表达式类
- 将表达式的集合拼接进PlainSelect的where条件中,转换成String重新返回到batis的属性
参考资料
作者: OnlyWaitY 发表日期:2021 年 7 月 20 日